package com.allwees.support.payment.payby;

import cn.hutool.core.io.FileUtil;
import com.allwees.bs.core.modelbase.constant.ResultEnum;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.core.common.context.SpringContextHolder;
import com.allwees.support.payment.model.PaymentInfo;
import com.allwees.support.payment.model.RefundInfo;
import com.allwees.support.payment.model.TransferInfo;
import com.allwees.support.payment.payby.builder.PlaceOrderRequestBuilder;
import com.allwees.support.payment.payby.builder.PlaceRefundOrderRequestBuilder;
import com.allwees.support.payment.payby.builder.PlaceTransferRequestBuilder;
import com.allwees.support.payment.payby.client.PayByClients;
import com.allwees.support.payment.payby.configuration.PaybyProperties;
import com.google.common.collect.Maps;
import com.payby.gateway.openapi.SgsApi;
import com.payby.gateway.openapi.SgsHead;
import com.payby.gateway.openapi.SgsRequestWrap;
import com.payby.gateway.openapi.SgsResponseWrap;
import com.payby.gateway.openapi.model.InappSignContent;
import com.payby.gateway.openapi.request.OrderIndexRequest;
import com.payby.gateway.openapi.request.PlaceOrderRequest;
import com.payby.gateway.openapi.request.PlaceRefundOrderRequest;
import com.payby.gateway.openapi.request.PlaceTransferToBankOrderRequest;
import com.payby.gateway.openapi.response.*;
import com.payby.gateway.sdk.misc.util.RsaUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.Charset;
import java.util.Map;

@Slf4j
public class PaybyAccountStrategy {
    private static PaybyProperties paybyProperties = SpringContextHolder.getBean(PaybyProperties.class);

    public static Map<String, String> create(PaymentInfo paymentInfo) {
        PlaceOrderRequest request = PlaceOrderRequestBuilder.build(paymentInfo);
        SgsHead sgsHead;
        PlaceOrderResponse body;
        Map<String, String> retMap = Maps.newHashMap();
        try {
            log.info("create order to payby req:{}", request);
            SgsRequestWrap<PlaceOrderRequest> wrap = SgsRequestWrap.wrap(request);
            SgsResponseWrap<PlaceOrderResponse> responseWrap = PayByClients.getPayByClient().execute(SgsApi.PLACE_ACQUIRE_ORDER, wrap);
            sgsHead = responseWrap.getHead();
            body = responseWrap.getBody();
        } catch (Exception ex) {
            log.error(">>>>>>>>>> payby create", ex);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYPAL_CREATE_ERROR);
        }
        if (null == sgsHead || !sgsHead.isSuccess()) {
            log.error(">>>>>>>> payby create is not approved status:{},code:{},order uuid：{}"
                    , sgsHead.getApplyStatus(), sgsHead.getCode(), paymentInfo.getOrderUuid());
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYPAL_CREATE_ERROR);
        }

        String token = body.getInterActionParams().get("tokenUrl");
        retMap.put("token", token);

        if (paymentInfo.getPlatform().isApp()) {
            retMap.put("signedToken", sign(token, paymentInfo.getDeviceId()));
        }

        log.info("PaybyAccountStrategy create result:{}", retMap);

        return retMap;
    }

    public static String refund(RefundInfo refundInfo) {
        PlaceRefundOrderRequest request = PlaceRefundOrderRequestBuilder.build(refundInfo);
        SgsHead sgsHead;
        PlaceRefundOrderResponse body;
        try {
            log.info("refund to payby req:{}", request);
            SgsRequestWrap<PlaceRefundOrderRequest> wrap = SgsRequestWrap.wrap(request);
            SgsResponseWrap<PlaceRefundOrderResponse> responseWrap = PayByClients.getPayByClient().execute(SgsApi.PLACE_REFUND_ORDER, wrap);
            sgsHead = responseWrap.getHead();
            body = responseWrap.getBody();
        } catch (Exception e) {
            log.error(">>>>>>>>>> payby refund", e);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_REFUND_PAYMENT_NOT_APPROVED);
        }
        if (null == sgsHead || !sgsHead.isSuccess()) {
            log.error(">>>>>>>> payby refund is not approved status:{},code:{},order uuid：{}"
                    , sgsHead.getApplyStatus(), sgsHead.getCode(), refundInfo.getOrderNo());
            throw BusinessException.getInstance(ResultEnum.PAYMENT_REFUND_PAYMENT_NOT_APPROVED);
        }

        return body.getRefundOrder().getOrderNo();
    }

    public static String transfer2Bank(TransferInfo transferInfo) {
        SgsHead sgsHead;
        PlaceTransferToBankOrderResponse body;
        try {
            PlaceTransferToBankOrderRequest request = PlaceTransferRequestBuilder.build(transferInfo);
            log.info("transfer2Bank from payby req:{}", request);
            SgsRequestWrap<PlaceTransferToBankOrderRequest> wrap = SgsRequestWrap.wrap(request);
            SgsResponseWrap<PlaceTransferToBankOrderResponse> responseWrap = PayByClients.getPayByClient().execute(SgsApi.PLACE_TRANSFER_TO_BANK_ORDER, wrap);
            sgsHead = responseWrap.getHead();
            body = responseWrap.getBody();
        } catch (Exception e) {
            log.error(">>>>>>>>>> payby transfer", e);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_TRANSFER_ERROR);
        }
        if (null == sgsHead || !sgsHead.isSuccess()) {
            log.error(">>>>>>>> payby transfer failed, status:{},code:{},orderNo：{}"
                    , sgsHead.getApplyStatus(), sgsHead.getCode(), transferInfo.getMerchantOrderNo());
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_TRANSFER_ERROR);
        }

        return body.getTransferToBankOrder().getOrderNo();
    }

    public static String transfer2BankResultQuery(String merchantOrderNo) {
        SgsHead sgsHead;
        GetTransferToBankOrderResponse body;
        try {
            OrderIndexRequest request = new OrderIndexRequest();
            // Merchant order number Required
            request.setMerchantOrderNo(merchantOrderNo);

            log.info("transfer2BankResultQuery req:{}", request);
            SgsRequestWrap<OrderIndexRequest> wrap = SgsRequestWrap.wrap(request);
            SgsResponseWrap<GetTransferToBankOrderResponse> responseWrap = PayByClients.getPayByClient().execute(SgsApi.GET_TRANSFER_TO_BANK_ORDER, wrap);
            sgsHead = responseWrap.getHead();
            body = responseWrap.getBody();
        } catch (Exception e) {
            log.error(">>>>>>>>>> payby transfer query", e);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_TRANSFER_QUERY_ERROR);
        }
        if (null == sgsHead || !sgsHead.isSuccess()) {
            log.error(">>>>>>>> payby transfer query failed, status:{},code:{},order uuid：{}"
                    , sgsHead.getApplyStatus(), sgsHead.getCode(), merchantOrderNo);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_TRANSFER_QUERY_ERROR);
        }

        Map<String, String> resultMap = Maps.newHashMap();

        String status = body.getTransferToBankOrder().getStatus();
        if (StringUtils.equals(status, "SUCCESS")) {
            resultMap.put("status", "SUCCESS");
        } else if (StringUtils.equals(status, "FAILURE") || StringUtils.equals(status, "BANK_FAIL")) {
            resultMap.put("status", "FAILED");
        } else if (StringUtils.equals(status, "CREATED")) {
            resultMap.put("status", "ING");
        } else {
            log.warn("order uuid：{},unknown status:", merchantOrderNo, status);
            resultMap.put("status", "ING");
        }

        resultMap.put("msg", body.getTransferToBankOrder().getFailCode());

        return body.getTransferToBankOrder().getOrderNo();
    }

    public static Map<String, String> queryResult(String merchantOrderNo) {
        Map<String, String> resultMap = Maps.newHashMap();
        OrderIndexRequest request = new OrderIndexRequest();
        request.setMerchantOrderNo(merchantOrderNo);
        SgsHead sgsHead;
        GetPlaceOrderResponse body;
        try {
            log.info("queryResult to payby req:{}", request);
            SgsRequestWrap<OrderIndexRequest> wrap = SgsRequestWrap.wrap(request);
            SgsResponseWrap<GetPlaceOrderResponse> responseWrap = PayByClients.getPayByClient().execute(SgsApi.GET_ACQUIRE_ORDER, wrap);
            sgsHead = responseWrap.getHead();
            body = responseWrap.getBody();
        } catch (Exception e) {
            log.error(">>>>>>>>>> payby queryResult", e);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_QUERY_ERROR);
        }
        if (!sgsHead.isSuccess()) {
            log.error(">>>>>>>> payby query failed,status:{},code:{},order uuid：{}"
                    , sgsHead.getApplyStatus(), sgsHead.getCode(), merchantOrderNo);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYBY_QUERY_ERROR);
        }

        // PAID_SUCCESS,SETTLED,CREATED,FAILURE
        String status = body.getAcquireOrder().getStatus();
        if (StringUtils.equals(status, "PAID_SUCCESS") || StringUtils.equals(status, "SETTLED")) {
            resultMap.put("status", "PAYED");
        } else {
            resultMap.put("status", status);
        }

        String msg = "";
        if (StringUtils.isNotBlank(body.getAcquireOrder().getFailCode())) {
            msg = body.getAcquireOrder().getFailCode();
            if (StringUtils.isNotBlank(body.getAcquireOrder().getFailDes())) {
                msg = msg + ":" + body.getAcquireOrder().getFailDes();
            }
        }

        resultMap.put("msg", msg);

        return resultMap;
    }


    public static String sign(String token, String deviceId) {
        InappSignContent content = new InappSignContent();
        // iapAppId Required
        content.setIapAppId(paybyProperties.getAppId());
        // iapDeviceId Required
        content.setIapDeviceId(deviceId);
        // partnerId Required
        content.setIapPartnerId(paybyProperties.getPartnerId());
        // token Required
        content.setToken(token);
        String signedStr;
        try {
            String merchantPrivateKey = FileUtil.readUtf8String(paybyProperties.getMerchant_private_key_path());
            signedStr = RsaUtil.sign(content, Charset.forName("UTF-8"), merchantPrivateKey);
        } catch (Exception e) {
            log.error(">>>>>>>>>> payby sign", e);
            throw BusinessException.getInstance(ResultEnum.PAYMENT_PAYPAL_CREATE_ERROR);
        }

        return signedStr;
    }


//    private static Map<String, String> _createResult(PlaceOrderResponse response) {
//        Map<String, String> map = new HashMap<>(2);
//        map.put("id", response.getInterActionParams().get("tokenUrl"));
////        LinkDescription link = response.result().links().stream().filter(l -> l.rel().equals("approve")).findFirst().get();
////        map.put("approveUrl", link.href());
//
//        return map;
//    }
}
